# -*- coding: utf-8 -*-
"""

### à ajouter en 2eme ligne pour profiling : # cython: profile=True

#-------------------------------------------------------------------------------
# Nom:         requete_bv_amont
# Auteur:      Alain Gauthier
#
# Créé le:     26/02/2013
# Copyright:   (c) eaucéa 2014
# Licence:     GNU Public Licence (GPL)

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

Construction des sous bassins versant amont de chaque noeud hydrographique
à partir des bv élémentaires

Ce script doit être lancé depuis la console python de QGIS

#-------------------------------------------------------------------------------
"""

# constantes désignant les attributs utilisés
__ID_BDCARTH__ = 'ID_BDCARTH'
__ID_ND_INI__  = 'ID_ND_INI'
__ID_ND_FIN__  = 'ID_ND_FIN'
__STRAHLER__   = 'STRAHLER'
__CODE_SECT__  = 'CODE_SECT'
__CODE_SOUS_SECT__  = 'C_SS_SECT'
__ID_BVELEM__ = 'GRIDCODE'

# constantes utilisés pour la couche résultat
__COUCHE_SS_BV_AMONT__ = 'SS_BV_amont_par_noeud.shp'
__ID_ND__              = 'ID_ND_BDCA'

# constante d'approximation de distance entre noeud hydrographique et frontière de BV élémentaire (en m)
__DIST_NOEUD_BV__ = 80

# compilation C
import cython

# modules externes
import numpy as np
# modules système
import traceback
import sys
import os
# modules QGIS
import shapely.geometry
import shapely.wkb
import shapely.wkt
import shapely.ops
import shapely.prepared
from shapely import speedups

# modules GUI
from PyQt4 import QtCore, QtGui

# constante de nom de fichier log
__fichier_log__= os.path.join(os.getenv('TEMP'),"modelepluiedebit_bv_calcul.log")

# shapely speedup
speedups.enable()

#------------------------------------------------------------------------------#

class requetes_qgis:
    """
    Classe pouvant lancer des requêtes spécifiques avec QGIS
    chaque méthode lance une requête et renvoie le résultat dans une couche
    """

    #--------------------------------------------------------------------------#

    def __init__(self,Aqgis):
        """
        constructeur passant le module de QGIS en paramètre
        """
        self._iface = Aqgis.utils.iface
        self._core  = Aqgis.core
        self._moduleGeometry = shapely.geometry
        self._wkb = shapely.wkb
        self._wkt = shapely.wkt
        self._geoOps = shapely.ops
        self._geoPrepared = shapely.prepared
        # couche des BV élémentaires
        self._coucheBVElem = None
        # couche des noeuds hydro
        self._coucheNoeuds = None
        # couche des tronçons hydro
        self._coucheTroncons = None
        # couche des secteurs hydrographiques
        self._coucheSecteurs = None
        self._coucheSousSecteurs = None
        # progress bar
        self.setprogressBar(None)

    #--------------------------------------------------------------------------#

    def setprogressBar(self,AprogressBar):
        """
        gestion de la barre de progression
        """
        self.progressBar = AprogressBar

    #--------------------------------------------------------------------------#

    def ajusterProgressBar(self,Avaleur,Amax):
        """
        ajustement de la jauge en utilisant le rapport Avaleur/Amax
        """
        if Amax==0:
            self.progressBar.setMaximum(0)
            self.progressBar.setValue(0)
        else:
            proportion=100*Avaleur/Amax
            self.progressBar.setValue(proportion)
            QtGui.QApplication.processEvents()

    #--------------------------------------------------------------------------#

    def coucheParId(self, AIDcouche):
        """
        renvoie la couche ouverte dont l'identifiant est AIDCouche
        """
        return self._core.QgsMapLayerRegistry.instance().mapLayer(str(AIDcouche))

    #--------------------------------------------------------------------------#

    def set_coucheNoeud(self, AIDcouche):
        """
        Initialisation de la couche des noeuds
        """
        self._coucheNoeuds=self.coucheParId(AIDcouche)

    #--------------------------------------------------------------------------#

    def set_coucheTroncon(self, AIDcouche):
        """
        Initialisation de la couche des tronçons
        """
        self._coucheTroncons=self.coucheParId(AIDcouche)

    #--------------------------------------------------------------------------#

    def set_coucheSousSecteur(self, AIDcouche):
        """
        Initialisation de la couche des sous-secteurs
        """
        self._coucheSousSecteurs=self.coucheParId(AIDcouche)

    #--------------------------------------------------------------------------#

    def set_coucheSecteur(self, AIDcouche):
        """
        Initialisation de la couche des secteurs
        """
        self._coucheSecteurs=self.coucheParId(AIDcouche)

    #--------------------------------------------------------------------------#

    def set_coucheBVElem(self, AIDcouche):
        """
        Initialisation de la couche des BV élémentaires
        """
        self._coucheBVElem=self.coucheParId(AIDcouche)

    #--------------------------------------------------------------------------#

    def afficherCouches(self):
        """
        affiche la liste des couches chargées dans QGIS
        """
        layers = self._iface.legendInterface().layers()

        print(layers)
        for layer in layers:
            layerName=layer.name()
            print(layerName)

    #--------------------------------------------------------------------------#

    def creerCoucheSousBVAmont(self,AnomCouche,AnomCodeNoeud):
        """
        renvoie la couche vectorielle qgis devant accueillir les sous bassins versant amont de
        chaque noeud du réseau de la bd Carthage.
        le nom de la couche est dans AnomCouche,
        la référence du noeud des entités est dans AnomCodeNoeud
        """
        # nouvelle couche avec système de coordonnées EPSG:3182 (GR96 / UTM zone 22N)
        resultat = self._core.QgsVectorLayer("Polygon?crs=epsg:3182&field=" + AnomCodeNoeud + ":integer&index=yes", AnomCouche, "memory")
        return resultat

    #--------------------------------------------------------------------------#

    def entites_selectionnees(self,Acouche):
        """
        Renvoie la liste des entités sélectionnées de la couche Acouche
        """
        resultat=[]
        ensemble_selec = Acouche.selectedFeatures()
        for entite in ensemble_selec:
            copie = self._core.QgsFeature(entite)
            resultat.append(copie) # attention à bien faire une copie
        return resultat

    #--------------------------------------------------------------------------#
    def selectionSecteur(self):
        """
        renvoie les secteurs sélectionnés de la couche Secteur sous forme de liste d'entités
        """
        return self.entites_selectionnees(self._coucheSecteurs)

    #--------------------------------------------------------------------------#

    def selectionSousSecteur(self):
        """
        renvoie les sous-secteurs sélectionnés de la couche sous-Secteur sous forme de liste d'entités
        """
        return self.entites_selectionnees(self._coucheSousSecteurs)

    #--------------------------------------------------------------------------#

    def enregistrerFichierCouche(self, Acouche, AnomFichier):
        """
        ouverture du fichier AnomFichier associé à la couche Acouche
        """
        error = self._core.QgsVectorFileWriter.writeAsVectorFormat(Acouche, AnomFichier, "utf-8", None, "ESRI Shapefile")
        resultat = True
        if error != self._core.QgsVectorFileWriter.NoError:
            print("Echec d'ecriture du fichier " + AnomFichier)
            resultat = False
        return resultat

    #--------------------------------------------------------------------------#

    def sousBVAmontParNoeud(self,AcheminBVCalcul):
        """
        Renvoie pour chacun des noeuds hydrographiques les sous bassins versant
        composés des BV élémentaires
        La requête se fait en se basant sur les intersections des tronçons amont des noeuds et des
        BV élémentaires
        La couche créée est sauvegardée à partir du chemin AcheminBVCalcul passé en paramètre
        """
        # declarations C
        cdef int idNoeud
        cdef int index
        cdef int compte

        try:
            self.obFichier = open(__fichier_log__,'w')

            # couche vectorielle contenant les sous bv par noeud
            coucheSousBVAmont = self.creerCoucheSousBVAmont(__COUCHE_SS_BV_AMONT__, __ID_ND__)

            # noeuds sélectionnés par l'utilisateur (interface QGIS)
            noeudsSelectionnes= self.entites_selectionnees(self._coucheNoeuds)
            filtreNoeuds=[]
            for entite in noeudsSelectionnes:
                attributs = entite.attributes()
                filtreNoeuds.append(attributs[self._coucheNoeuds.fieldNameIndex(__ID_BDCARTH__)])

            self.obFichier.write('filtre noeuds selectionnes : ' + str(filtreNoeuds)+'\n')

            # liste des secteurs sélectionnés par l'utilisateur
            secteurSelectionne = self.selectionSecteur()

            # il faut sélectionner au moint un secteur
            if len(secteurSelectionne)==0:
                QtGui.QMessageBox.information(None,QtGui.QApplication.translate('widgetDonnees','Information'), \
                    QtGui.QApplication.translate('widgetDonnees',u'Il faut sélectionner au moins un secteur dans QGIS'))


            tabGeomSelect = []
            for feature in secteurSelectionne:
                tabGeomSelect.append(self._wkb.loads(feature.geometry().asWkb()))
            geomSecteurSelectionne = self._geoOps.cascaded_union(tabGeomSelect)
            del(tabGeomSelect)

            # ss secteurs à traiter sélectionnés par l'utilisateur (interface QGIS)
            dicoIDNoeudsSousSecteur = None
            sousSecteurSelectionne = self.selectionSousSecteur()
            if len(sousSecteurSelectionne)>0:
                geomSousSecteurSelectionne = self._wkb.loads(sousSecteurSelectionne[0].geometry().asWkb())
                # sélection des noeuds avec leur ID (référencé par index de champ)
                dicoIDNoeudsSousSecteur,\
                dicoGeomNoeudSousSecteur = self.valeursChampsEtGeometrieDeCouche(self._coucheNoeuds,
                                                                                   [__ID_BDCARTH__],
                                                                                   __ID_BDCARTH__,
                                                                                   geomSousSecteurSelectionne)

            # sélection des noeuds avec leur ID (référencé par index de champ)
            dicoIDNoeudsSecteur,\
            dicoGeomNoeud = self.valeursChampsEtGeometrieDeCouche(self._coucheNoeuds,
                                                                   [__ID_BDCARTH__],
                                                                   __ID_BDCARTH__,
                                                                   geomSecteurSelectionne)

            indexIDNoeud = self._coucheNoeuds.fieldNameIndex(__ID_BDCARTH__)

            # géométrie des tronçons
            dicoDonneesTroncons,\
            dicoGeomTroncons = self.valeursChampsEtGeometrieDeCouche(self._coucheTroncons,
                                                                     [__ID_ND_INI__,
                                                                      __ID_ND_FIN__,
                                                                      __STRAHLER__,
                                                                      __ID_BDCARTH__],
                                                                     __ID_BDCARTH__,
                                                                     geomSecteurSelectionne)

            self.obFichier.write('nb troncons selectionnes : ' + str(len(dicoGeomTroncons.keys()))+'\n')
            # dictionnaire de tronçons par noeud
            dicoTronconsParNoeudFin = self.tronconsParNoeudFin(dicoDonneesTroncons)
            # résultat par noeud
            dicoTroncons = dict()

            # parcours selon la sélection secteur/sous-secteur ou couche entière
            if dicoIDNoeudsSousSecteur==None:
                dicoIDNoeuds = dicoIDNoeudsSecteur
            else:
                dicoIDNoeuds = dicoIDNoeudsSousSecteur
            self.obFichier.write('nb noeuds selectionnes : ' + str(len(dicoIDNoeuds[indexIDNoeud]))+'\n')

            # progressbar
            compte = 0
            total=len(dicoIDNoeuds[indexIDNoeud])
            self.ajusterProgressBar(compte,total)
            for idNoeud in dicoIDNoeuds[indexIDNoeud]:
                if dicoTroncons.has_key(idNoeud):
                    compte+=1
                    self.ajusterProgressBar(compte,total)
                    continue
                if (len(filtreNoeuds) > 0)&(not idNoeud in filtreNoeuds):
                    compte+=1
                    self.ajusterProgressBar(compte,total)
                    continue
                # tronçons en amont du noeud
                self.tronconsAmontDeNoeud(dicoTronconsParNoeudFin,dicoDonneesTroncons,idNoeud,dicoTroncons)
                compte+=1
                self.ajusterProgressBar(compte,total)

            # libération memoire des objets intermédiaires
            del(dicoTronconsParNoeudFin)
            del(dicoDonneesTroncons)

            # réunion des BV par noeud identifié : le BV amont en un noeud est la réunion
            # des BV élémentaires contenant les troncons amont du noeud.

            # liste des géométries des BV élémentaires
            self.obFichier.write('lecture des geometries elementaires\n')

            if geomSousSecteurSelectionne != None:
                dicoBVElem,\
                dicoGeomBVElem = self.valeursChampsEtGeometrieDeCouche(self._coucheBVElem,
                                                                       [__ID_BVELEM__],
                                                                       __ID_BVELEM__,
                                                                       geomSousSecteurSelectionne)
            else:
                dicoBVElem,\
                dicoGeomBVElem = self.valeursChampsEtGeometrieDeCouche(self._coucheBVElem,
                                                                       [__ID_BVELEM__],
                                                                       __ID_BVELEM__,
                                                                       geomSecteurSelectionne)

            self.obFichier.write('fin de lecture : ' + str(len(dicoGeomBVElem.keys())) + ' elements\n')

            # boucle sur les tronçons amont de chaque noeud
            self.obFichier.write('sous bv de noeud\n')
            compte = 0
            total=len(dicoIDNoeuds[indexIDNoeud])
            self.ajusterProgressBar(compte,total)
            for idNoeud in dicoIDNoeuds[indexIDNoeud]:
                if (len(filtreNoeuds) > 0)&(not idNoeud in filtreNoeuds):
                    compte+=1
                    self.ajusterProgressBar(compte,total)
                    continue
                self.obFichier.write('noeud : ' + str(idNoeud)+'\n')
                # pas de tronçon correspondant
                if not dicoTroncons.has_key(idNoeud):
                    compte+=1
                    self.ajusterProgressBar(compte,total)
                    continue
                # pas de sous bassins versant si noeuds amont
                if len(dicoTroncons[idNoeud])==0:
                    compte+=1
                    self.ajusterProgressBar(compte,total)
                    continue
                # recherche des bv elementaires amont
                bv_trouves = self.rechBVdeNoeud(idNoeud,
                                                dicoTroncons,
                                                dicoGeomBVElem,
                                                dicoGeomNoeud,
                                                dicoGeomTroncons)
                if len(bv_trouves)==0:
                    compte+=1
                    self.ajusterProgressBar(compte,total)
                    continue
                # sous bassin versant : réunion des bv élémentaires trouvés
                sous_bv_trouve = self._geoOps.cascaded_union(bv_trouves)
                del(bv_trouves)
                #simplification du BV trouvé (20m de tolérance)
                sous_bv_trouve = sous_bv_trouve.simplify(20.0, preserve_topology=True)
                # tentative pour combler les trous
                if sous_bv_trouve.geom_type == 'Polygon':
                    sous_bv_trouve = self._moduleGeometry.Polygon(sous_bv_trouve.exterior)
                else:
                    lpoly=[]
                    for polyg in sous_bv_trouve:
                        lpoly.append(self._moduleGeometry.Polygon(polyg.exterior))
                    sous_bv_trouve = self._geoOps.cascaded_union(lpoly)
                # ajout à la couche des sous bassin versant
                if sous_bv_trouve == None:
                    self.obFichier.write("erreur : sous_bv_trouve = \n"+ str(sous_bv_trouve))
                else:
                    if not self.ajouterSousBV(coucheSousBVAmont,sous_bv_trouve,idNoeud,AcheminBVCalcul):
                        self.obFichier.write("echec d'ecriture dans le fichier resultat\n")
                compte = compte + 1
                if compte%10==0:
                    self.obFichier.flush()
                # progressbar
                self.ajusterProgressBar(compte,total)

            # enregistrement des derniers BV traités si nécessaire
            fournisseur = coucheSousBVAmont.dataProvider()
            if fournisseur.featureCount()%10 != 0:
                coucheSousBVAmont.commitChanges()
                if not self.enregistrerFichierCouche(coucheSousBVAmont, \
                                                     os.path.join(AcheminBVCalcul,__COUCHE_SS_BV_AMONT__)):
                    self.obFichier.write("echec de l'ecriture du fichier resultat\n")
                else:
                    self.obFichier.write("ecriture du fichier : "+\
                                         os.path.join(AcheminBVCalcul,__COUCHE_SS_BV_AMONT__)+\
                                         "\n")
            # la couche des sous bv amont est rendue visible
            # self._core.QgsMapLayerRegistry.instance().addMapLayers([coucheSousBVAmont])
            self.obFichier.close()
        except Exception, e:
            exc_type, exc_value, exc_traceback = sys.exc_info()
            traceback.print_exc(None,sys.stdout)
            self.obFichier.write('-'*80+'\n')
            self.obFichier.write('exception : ' + str(e)+'\n')
            self.obFichier.write(repr(traceback.extract_tb(exc_traceback)))
            self.obFichier.write('\n'+'-'*80+'\n')
            self.obFichier.close()

    #--------------------------------------------------------------------------#

    def ajouterSousBV(self,AcoucheSousBVAmont,Asous_bv,ANoeud,AcheminBVCalcul):
        """
        Ajout du sous bv à la couche des sous bv pour le noeud aNoeud
        """
        # fournisseur de données
        fournisseur = AcoucheSousBVAmont.dataProvider()
        # nouvelle entité
        entite = self._core.QgsFeature()
        # sa géométrie
        entite.setGeometry(self._core.QgsGeometry.fromWkt(Asous_bv.wkt))
        # son code noeud
        entite.setAttributes([ANoeud])
        # ajout de l'entité à la couche
        resultat = fournisseur.addFeatures([entite])
        # sauvegarde si nécessaire
        nbEntites = fournisseur.featureCount()
        # fichier associé à la couche des sous BV amont
        if nbEntites%10 == 0:
            AcoucheSousBVAmont.commitChanges()
            resultat = self.enregistrerFichierCouche(AcoucheSousBVAmont,os.path.join(AcheminBVCalcul,__COUCHE_SS_BV_AMONT__))
        return resultat


    #--------------------------------------------------------------------------#

    def rechBVdeNoeud(self,AcleNoeud,AdicoTroncons,AdicoGeomBVElem,AdicoGeomNoeud,AdicoGeomTroncons):
        """
        renvoie la liste des géométries des BV élémentaires retenus en amont du noeud AcleNoeud
        La recherche se fait dans AdicoGeomBVElem
        Les tronçons amont du noeud AcleNoeud se trouvent dans AdicoTroncons[AcleNoeud]
        La géométrie des tronçons se trouve dans AdicoGeomTroncons[idTroncon]
        Le point géométrique du noeud AcleNoeud se trouvent dans AdicoGeomNoeud[AcleNoeud]
        Algorithme de calcul : module shapely
        """
        # déclarations C
        cdef int idTroncon

        # résultat
        resultat = []

        # lignes des tronçons
        liste_lignes = []
        for idTroncon in AdicoTroncons[AcleNoeud]:
            ligne = AdicoGeomTroncons[idTroncon]
            # cas anormal ne devant pas arriver (arrêter le calcul ?)
            if ligne.type == 'Polygon':
                continue
            liste_lignes.append(ligne)
        # les noeuds doivent être considérés comme des cercle et non des points
        # afin de faire comme si chaque noeud était sur l'intersection des BV
        pointNoeud = AdicoGeomNoeud[AcleNoeud]
        cercleNoeud = pointNoeud.buffer(__DIST_NOEUD_BV__) # hypothèse de distance: __DIST_NOEUD_BV__ m
        # on retranche le cercle à la réunion des lignes amont du noeud
        union_lignes_total = self._moduleGeometry.MultiLineString(liste_lignes)
        del(liste_lignes)
        union_lignes = union_lignes_total.difference(cercleNoeud)
        del(union_lignes_total)

        # recherche des bv contenant les lignes
        prep_union_lignes = self._geoPrepared.prep(union_lignes)
        resultat = filter(prep_union_lignes.intersects,AdicoGeomBVElem.values())

        del(union_lignes)

        # renvoie le résultat
        return resultat

    #--------------------------------------------------------------------------#

    def tronconsAmontDeNoeud(self,AdicoTronconsParNoeudFin,AdicoDonneesTroncons,AidNoeud,AdicoTroncons):
        """
        renvoie l'ensemble des ID de tronçons de AdicoDonneesTroncons
        en amont du noeud AidNoeud dans l'élément du dictionnaire AdicoTroncons[AidNoeud]
        Algorithme utilisant les données stockées dans le dictionnaire dicoTronconsParNoeudFin plutôt que dans la couche QGIS
        """
        # declarations C
        cdef int indice
        cdef int ires
        cdef int strahler
        cdef int noeudIni
        cdef int idtroncon

        # recherche selon l'index du champ des noeuds finaux __ID_ND_FIN__ :
        # indices selon les valeurs de __ID_ND_FIN__
        try:
            listeTronconsCherches = AdicoTronconsParNoeudFin[AidNoeud]
        except:
            listeTronconsCherches = []
        # resultat
        AdicoTroncons[AidNoeud]=np.empty(len(listeTronconsCherches),np.int)

        # index des clefs dans AdicoDonneesTroncons
        indexTroncon   = self._coucheTroncons.fieldNameIndex(__ID_BDCARTH__)
        indexStrahler  = self._coucheTroncons.fieldNameIndex(__STRAHLER__)
        indexNoeudIni  = self._coucheTroncons.fieldNameIndex(__ID_ND_INI__)
        # recherche des tronçons amont dans les résultats
        ires = 0
        for indice in listeTronconsCherches:
            idtroncon = AdicoDonneesTroncons[indexTroncon][indice]
            strahler  = AdicoDonneesTroncons[indexStrahler][indice]
            noeudIni  = AdicoDonneesTroncons[indexNoeudIni][indice]
            # ajout des tronçons au résultat
            AdicoTroncons[AidNoeud][ires] = idtroncon
            ires = ires+1
            # pas de recherche si le noeud est déjà traité
            # afluent amont seulement pour les valeurs de Strahler > 1
            if (strahler>1)&(not AdicoTroncons.has_key(noeudIni)):
                self.tronconsAmontDeNoeud(AdicoTronconsParNoeudFin,AdicoDonneesTroncons,noeudIni,AdicoTroncons)
        # réunion des tronçons amont du noeud courant et ceux de noeudIni
        for indice in listeTronconsCherches:
            noeudIni  = AdicoDonneesTroncons[indexNoeudIni][indice]
            if AdicoTroncons.has_key(noeudIni):
                AdicoTroncons[AidNoeud] = np.lib.union1d(AdicoTroncons[AidNoeud],AdicoTroncons[noeudIni])

    #--------------------------------------------------------------------------#

    def tronconsParNoeudFin(self,AdicoDonneesTroncons):
        """
        renvoie le dictionnaire des tronçons par noeud final :
        le résultat est sous la forme resultat[idnoeud] = [i1,i2...] de telle sorte que
        AdicoDonneesTroncons[] = liste[i] soit le i eme troncon de noeud final (aval) idnoeud
        """
        # déclarations C
        cdef int i

        # résultat
        resultat = dict()
        # boucle sur tous les noeuds aval de tous les tronçons
        indexNoeudFin  = self._coucheTroncons.fieldNameIndex(__ID_ND_FIN__)
        for i in range(len(AdicoDonneesTroncons[indexNoeudFin])):
            idNoeudFin = AdicoDonneesTroncons[indexNoeudFin][i]
            if resultat.has_key(idNoeudFin):
                liste_troncons = resultat[idNoeudFin]
            else:
                liste_troncons = []
                resultat[idNoeudFin] = liste_troncons
            liste_troncons.append(i)
        # renvoie le résultat
        return resultat

    #--------------------------------------------------------------------------#

    def valeursChampsDeCouche (self, Acouche, Achamps):
        """
        renvoie le dictionnaire dic[index]=[valeurs] de la couche Acouche pour la liste de champs Achamps
        avec une clef par index de Achamps et un tableau de valeurs de longueur du nombre d'entités
        """
        resultat = dict()

        # index des champs
        listeAttributs = []
        for champ in Achamps:
            index = Acouche.fieldNameIndex(champ)
            listeAttributs.append(index)
            # structure de sortie
            resultat[index] = []

        # récupération des valeurs auprès de Acouche
        for entite in Acouche.getFeatures():
            tab = entite.attributes()
            for index in listeAttributs:
                resultat[index].append(tab[index])
        return resultat

    #--------------------------------------------------------------------------#

    def valeursChampsEtGeometrieDeCouche (self, Acouche, Achamps, Aident,AFiltreGeom=None):
        """
        renvoie :
            - le dictionnaire dic[index]=[valeurs] de la couche Acouche pour la liste de champs Achamps
            avec une clef par index de Achamps et un tableau de valeurs de longueur du nombre d'entités
            - le dictionnaire dic[identifiant]=geometrie de la couche Acouche rangé par identifiant Aident
        Option : AFiltreGeom permet de sélectionner un partie des données contenue dans AFiltreGeom
        """
        # declarations C
        cdef int index
        cdef int idx
        cdef int indexIdent

        # index de l'ident de géométrie
        indexIdent = Acouche.fieldNameIndex(Aident)
        indexIdentTrouve = False
        # index des champs
        listeAttributs = []
        for champ in Achamps:
            index = Acouche.fieldNameIndex(champ)
            listeAttributs.append(index)
            # recherche de l'index Aident
            if index==indexIdent:
                indexIdentTrouve = True
        # ajout de l'index Aident si non trouvé
        if not indexIdentTrouve:
            listeAttributs.append(indexIdent)

        if AFiltreGeom != None:
            limites = AFiltreGeom.bounds
            rectangleSelection = self._core.QgsRectangle(limites[0],limites[1],limites[2],limites[3])
            Acouche.select(rectangleSelection,False)
        else:
            Acouche.selectAll()

        # structures de sortie
        resDicoChamps = dict()
        nbEntites = Acouche.featureCount()
        # attributs
        for index in listeAttributs:
            resDicoChamps[index] = np.empty(nbEntites,np.int)
        # géométrie
        resDicoGeom   = dict()

        # récupération des valeurs auprès de Acouche avec filtre précis
        # On laisse de côté ??? -> algorithme utilisant la biblio GEOS qui est très lente !
        prep_geomFiltre = None
##        if AFiltreGeom != None:
##            prep_geomFiltre = self._geoPrepared.prep(AFiltreGeom)
        idx = 0
        # progressbar
        total=Acouche.selectedFeatureCount()
        self.ajusterProgressBar(idx,total)
        for entite in Acouche.selectedFeatures():
            geom_entite = self._wkb.loads(entite.geometry().asWkb())
            # test de sélection de l'entité selon le filtre de géométrie
            # On laisse de côté ??? -> algorithme utilisant la biblio GEOS qui est très lente !
            if (prep_geomFiltre!=None):
                if (not prep_geomFiltre.intersects(geom_entite)):
                    continue

            # dictionnaire des clefs d'index de l'attribut pointant sur les valeurs des champs
            tab = entite.attributes()
            for index in listeAttributs:
                resDicoChamps[index][idx] = tab[index]
            # index d'entité suivant
            idx=idx+1
            self.ajusterProgressBar(idx,total)
            # dictionnaire des objets géométriques (module shapely) pour l'index identifiant
            cle_chaine = tab[indexIdent]
            resDicoGeom[cle_chaine] = geom_entite
        # extraction du résultat
        for index in listeAttributs:
            resDicoChamps[index] = resDicoChamps[index][:idx]
        return resDicoChamps,resDicoGeom

    #--------------------------------------------------------------------------#

    def rechWhere(self, Acouche, AchaineExp):
        """
        renvoie la liste des entités satisfaisant "where Aexp" dans la couche Acouche
        """
        # analyseur d'expressions
        exp = self._core.QgsExpression(AchaineExp)
        if exp.hasParserError():
            raise TypeError(exp.parserErrorString())
        # préparation des données
        exp.prepare(Acouche.pendingFields())
        # boucle renvoyant chaque entité trouvée satisfaisant Aexp
        for entite in Acouche.getFeatures():
            res = exp.evaluate(entite)
            if exp.hasEvalError():
                raise ValueError(exp.evalErrorString())
            if bool(res):
                yield self._core.QgsFeature(entite) # attention à bien faire une copie

    #--------------------------------------------------------------------------#

    def coucheDeNom(self,Anom):
        """
        Renvoie la couche de nom aNom chargée dans QGIS
        """
        resultat = None
        layers = self._iface.legendInterface().layers()
        # boucle sur les couches
        for layer in layers:
            layerName=layer.name()
            if (layerName == Anom):
                # c'est la bonne couche
                resultat = layer
                break
        return resultat

if __name__ == '__main__':
    raise EnvironmentError("module devant être exécuté sous console/extension python QGIS")
